/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2009, Around(J2)ME				                                 **
 **   All rights reserved.                                                   **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the New BSD License which accompanies this    			 **
 **   distribution.											                 **
 **   A copy is found in the textfile LICENSE.txt							 **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Main developers:                                                       **
 **     Juri Strumpflohner		http://blog.js-development.com	             **
 **		Matthias Braunhofer		http://matthias.jimdo.com                    **
 **                                                                          **
 **  **********************************************************************  */

package com.aroundj2me.xml;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Vector;

import javax.microedition.lcdui.Alert;
import javax.microedition.location.AddressInfo;
import javax.microedition.location.Landmark;
import javax.microedition.location.QualifiedCoordinates;

import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import com.aroundj2me.model.Constants;
import com.aroundj2me.model.Place;
import com.aroundj2me.utility.AlertCreator;
import com.aroundj2me.utility.StringUtils;

/**
 * Class responsible for the decoding of the XML files received from the server
 * 
 */
public class XMLDecoder {
	private KXmlParser parser;
	private String xmlResponseString;
	private String type;
	private int size;
	private Vector places;
	private Alert alert;

	public XMLDecoder() {
		this.parser = new KXmlParser();
		this.places = new Vector();
	}

	/**
	 * Does the parsing stuff
	 * 
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	public void parseXML() throws XmlPullParserException, IOException {
		this.places.removeAllElements();

		ByteArrayInputStream s = new ByteArrayInputStream(xmlResponseString
				.getBytes());

		parser.setInput(s, "ISO-8859-1");
		parser.nextTag();
		parser.require(XmlPullParser.START_TAG, null, "root");
		parser.nextTag();
		parser.require(XmlPullParser.START_TAG, null, "type");
		// read type
		type = parser.nextText();
		parser.require(XmlPullParser.END_TAG, null, "type");
		parser.nextTag();
		parser.require(XmlPullParser.START_TAG, null, "size");
		// read size
		String intString = parser.nextText();
		if(!StringUtils.isNullOrEmpty(intString)){
			size = Integer.parseInt(intString);
		}else
			size = -1;
		parser.require(XmlPullParser.END_TAG, null, "size");
		parser.nextTag();
		parser.require(XmlPullParser.START_TAG, null, "responseBody");

		if (type.equals(Constants.DATA_PLACE))
			parsePlaces();
		else
			parseAlert();

		parser.require(XmlPullParser.END_TAG, null, "responseBody");
		parser.nextTag();
		parser.require(XmlPullParser.END_TAG, null, "root");
	}

	/**
	 * Parses an alert message
	 * 
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private void parseAlert() throws XmlPullParserException, IOException {
		String alertLevel = null;
		String alertMessage = "Cause: unknown";
		while (parser.nextTag() == XmlPullParser.START_TAG) {
			if (parser.getName().equals("level"))
				alertLevel = parser.nextText();
			else if (parser.getName().equals("message"))
				alertMessage = parser.nextText();
		}

		if (alertLevel.equals(Constants.ALERT_ERROR)) {
			alert = AlertCreator.getErrorAlert(alertMessage);
		} else {
			alert = AlertCreator.getInfoAlert(alertMessage);
		}
	}

	/**
	 * parses the places out of the XML file by creating {@link Place} objects
	 * added into a Vector
	 * 
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private void parsePlaces() throws XmlPullParserException, IOException {
		// parse out places
		while (parser.nextTag() == XmlPullParser.START_TAG) {
			Place p = new Place();
			QualifiedCoordinates coordinates = null;
			AddressInfo addressInfo = new AddressInfo();
			Landmark landmark = null;
			
			parser.require(XmlPullParser.START_TAG, null, "place");

			String name = "", address = "", postalcode = "", city = "", country = "", continent = "", phone = "", url = "";
			String[] categories = null;
			while (parser.nextTag() == XmlPullParser.START_TAG) {
				if (parser.getName().equals("name")) {
					name = parser.nextText();
				} else if (parser.getName().equals("address")) {
					address = parser.nextText();
				} else if (parser.getName().equals("postalCode")) {
					postalcode = parser.nextText();
				} else if (parser.getName().equals("city")) {
					city = parser.nextText();
				} else if (parser.getName().equals("country")) {
					country = parser.nextText();
				} else if (parser.getName().equals("continent")) {
					continent = parser.nextText();
				} else if (parser.getName().equals("phone")) {
					phone = parser.nextText();
				} else if (parser.getName().equals("url")) {
					url = parser.nextText();
				} else if (parser.getName().equals("categories")) {
					categories = parseCategories();
				} else if (parser.getName().equals("location")) {
					coordinates = parseLocation();
				}
			}
			
			//build address info
			addressInfo.setField(AddressInfo.STREET, address);
			addressInfo.setField(AddressInfo.POSTAL_CODE, postalcode);
			addressInfo.setField(AddressInfo.CITY, city);
			addressInfo.setField(AddressInfo.COUNTRY, country);
			addressInfo.setField(AddressInfo.PHONE_NUMBER, phone);
			addressInfo.setField(AddressInfo.URL, url);
			
			p.setContinent(continent);
			p.setCategories(categories);
			
			//build landmark information
			landmark = new Landmark(name, p.getCategoriesAsString(), coordinates, addressInfo);
			p.setLandmark(landmark);
		
			places.addElement(p);
			parser.require(XmlPullParser.END_TAG, null, "place");
		}
	}

	/**
	 * parses the location out of the XML file
	 * @return
	 * @throws NumberFormatException
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private QualifiedCoordinates parseLocation() throws NumberFormatException, XmlPullParserException, IOException {
		QualifiedCoordinates coord;
		String lat = "", lon = "";
		parser.require(XmlPullParser.START_TAG, null, "location");

		while (parser.nextTag() == XmlPullParser.START_TAG) {
			if (parser.getName().equals("lat")) {
				lat = parser.nextText();
			} else if (parser.getName().equals("lon")) {
				lon = parser.nextText();
			}
		}

		coord = new QualifiedCoordinates(Double.parseDouble(lat), Double
				.parseDouble(lon), 0f, 0f, 0f);

		parser.require(XmlPullParser.END_TAG, null, "location");

		return coord;
	}
	
	/**
	 * parses the categories out of the XML file
	 * @return
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private String[] parseCategories() throws XmlPullParserException, IOException {
		Vector categories = new Vector();
		String category = "";
		parser.require(XmlPullParser.START_TAG, null, "categories");

		while (parser.nextTag() == XmlPullParser.START_TAG) {
			parser.require(XmlPullParser.START_TAG, null, "category");
			category = parser.nextText();
			categories.addElement(category);
			parser.require(XmlPullParser.END_TAG, null, "category");
		}

		parser.require(XmlPullParser.END_TAG, null, "categories");

		String[] result = new String[categories.size()];
		categories.copyInto(result);
		return result;
	}

	/*
	 * Getters and Setters
	 */

	public String getXmlResponseString() {
		return xmlResponseString;
	}

	public void setXmlResponseString(String xmlResponseString) {
		this.xmlResponseString = xmlResponseString;
	}

	public Vector getPlaces() {
		return places;
	}

	public String getType() {
		return type;
	}

	public Alert getAlert() {
		return alert;
	}
}
